local Class = require('lua.lang.Class')
local LuaFunction = require('lua.vm.LuaFunction')
local Globals = require('lua.vm.Globals')
local UpValue = require('lua.vm.UpValue')
local LuaNil = require('lua.vm.LuaNil')

local pack = table.pack
local unpack = table.unpack

--- @class LuaClosure : LuaFunction
--- @field sourceFile string
--- @field p Prototype
--- @field upValues UpValue[]
--- @field globals Globals
local LuaClosure = Class {
	__extends = LuaFunction
}

--- @param p Prototype
--- @param env LuaValue
--- @return LuaClosure
function LuaClosure:new(p, env)
	--- @type LuaClosure
	local o = {}
	o.p = p
	o.globals = env:instanceof(Globals) and env or nil
	setmetatable(o, LuaClosure)
	o:initUpvalue1(env)
	return o
end

--- @param env LuaValue
--- @return void
function LuaClosure:initUpvalue1(env)
	local p = self.p
	if p.upvalues == nil or #p.upvalues == 0 then
		self.upValues = {}
	else
		self.upValues = {
			[0] = UpValue:new({ env }, 0)
		}
	end
end

--- @return boolean
function LuaClosure:isClosure()
	return true
end

--- @return LuaClosure
function LuaClosure:checkClosure()
	return self
end

--- @return LuaValue[]
function LuaClosure:getNewStack()
	--- @type LuaValue[]
	local stack = {}
	for i = 1, self.p.maxStackSize do
		stack[i - 1] = LuaNil._NIL
	end
	return stack
end

--- @param arg1 LuaValue?
--- @param arg2 LuaValue?
--- @param arg3 LuaValue?
--- @return LuaValue
function LuaClosure:call(arg1, arg2, arg3)
	arg1 = arg1 or LuaNil._NIL
	arg2 = arg2 or LuaNil._NIL
	arg3 = arg3 or LuaNil._NIL
	local stack = self:getNewStack()
	local numParams = self.p.numParams
	if numParams == 3 then
		stack[0] = arg1
		stack[1] = arg2
		stack[2] = arg3
	elseif numParams == 2 then
		stack[0] = arg1
		stack[1] = arg2
	elseif numParams == 1 then
		stack[0] = arg1
	end
end

--- @param stack LuaValue[]
--- @param varargs Varargs
--- @return Varargs
function LuaClosure:execute(stack, varargs)

end

return LuaClosure